package compoundDB.database.container;

import compoundDB.database.enumeration.*;

/**
 * A class used to construct, store and parse search options for the
 * DatabaseAccessor to use with its search function. Constructed via the static
 * factory methods.
 * 
 * @author Kohl Bromwich
 */
public final class SearchOption
{
	/**
	 * The type of search.
	 */
	private SearchOptionType m_type;
	
	/**
	 * The column to search on.
	 */
	private ColumnField m_column;
	
	/**
	 * The minimum search value.
	 */
	private Number m_min;
	
	/**
	 * The maximum search value.
	 */
	private Number m_max;
	
	/**
	 * The text search value.
	 */
	private String m_text;
	
	/**
	 * Creates a new SearchOption instance. Private constructor as it is
	 * intended to be built using the static factory within the class.
	 * 
	 * @param type
	 *            the type of search/filter to make using teh data.
	 * @param column
	 *            the column to make the search on.
	 * @param min
	 *            the minimum number value to filter by.
	 * @param max
	 *            the maximum number value to filter by.
	 * @param text
	 *            the string value to filter/search by.
	 */
	private SearchOption(SearchOptionType type, ColumnField column, Number min,
			Number max, String text)
	{
		m_type = type;
		m_column = column;
		m_min = min;
		m_max = max;
		m_text = text;
	}

	/**
	 * This method parses the data it contains, returning a String that can be used as part of an SQL query.
	 * 
	 * @return The String to be used as part of an SQL query.
	 */
	public String getParsedExpression()
	{
		switch (m_type)
		{
		case RANGE:
			return parseRange();
		case MIN:
			return parseMin();
		case MAX:
			return parseMax();
		case EQUALNUMBER:
			return parseEqual();
		case EQUALSTRING:
			return parseEqual();
		case CONTAINS:
			return parseContains();
		case STARTS_WITH:
			return parseStartsWith();
		case ENDS_WITH:
			return parseEndsWith();
		default:
			return "";
		}
	}
	
	/**
	 * Parses the data for a Range Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseRange()
	{
		return m_column.getName() + " >= " + m_min.toString() + " && "
				+ m_column.getName() + " <= " + m_max.toString();
	}

	/**
	 * Parses the data for a Min Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseMin()
	{
		return m_column.getName() + " >= " + m_min.toString();
	}

	/**
	 * Parses the data for a Max Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseMax()
	{
		return m_column.getName() + " <= " + m_max.toString();
	}

	/**
	 * Parses the data for an Equal Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseEqual()
	{
		if (m_text == null)
		{
			return m_column.getName() + " = " + m_min.toString();
		}
		else
		{
			return m_column.getName() + " = '" + m_text.toString() + "'";
		}
	}

	/**
	 * Parses the data for a Contains Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseContains()
	{
		return m_column.getName() + " LIKE '%" + m_text.toString() + "%'";
	}

	/**
	 * Parses the data for a StartsWith Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseStartsWith()
	{
		return m_column.getName() + " LIKE '" + m_text.toString() + "%'";
	}

	/**
	 * Parses the data for a EndsWith Search.
	 * 
	 * @return The parsed String.
	 */
	private String parseEndsWith()
	{
		return m_column.getName() + " LIKE '%" + m_text.toString() + "'";
	}
	
	/**
	 * Creates and returns a new instance of a Range SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param min
	 *            the minimum number value to search/filter by.
	 * @param max
	 *            the maximum number value to search/filter by.
	 * 
	 * @return a new instance of a Range SearchOption.
	 */
	public static SearchOption createSearchRange(ColumnField column,
			Number min, Number max)
	{
		if (!(column.getDataType().equals(DataType.DOUBLE)
				|| column.getDataType().equals(DataType.FLOAT)
				|| column.getDataType().equals(DataType.INTEGER)
				|| column.getDataType().equals(DataType.LONG)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.RANGE, column, min, max, null);
	}
	
	/**
	 * Creates and returns a new instance of a Min SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param min
	 *            the minimum number value to search/filter by.
	 * 
	 * @return a new instance of a Min SearchOption.
	 */
	public static SearchOption createSearchMin(ColumnField column, Number min)
	{
		if (!(column.getDataType().equals(DataType.DOUBLE)
				|| column.getDataType().equals(DataType.FLOAT)
				|| column.getDataType().equals(DataType.INTEGER)
				|| column.getDataType().equals(DataType.LONG)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.MIN, column, min, null, null);
	}
	
	/**
	 * Creates and returns a new instance of a Max SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param max
	 *            the maximum number value to search/filter by.
	 * 
	 * @return a new instance of a Max SearchOption.
	 */
	public static SearchOption createSearchMax(ColumnField column, Number max)
	{
		if (!(column.getDataType().equals(DataType.DOUBLE)
				|| column.getDataType().equals(DataType.FLOAT)
				|| column.getDataType().equals(DataType.INTEGER)
				|| column.getDataType().equals(DataType.LONG)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.MAX, column, null, max, null);
	}
	
	/**
	 * Creates and returns a new instance of a Equal SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param val
	 *            the number value to search/filter by.
	 * 
	 * @return a new instance of a Equal SearchOption.
	 */
	public static SearchOption createSearchEqual(ColumnField column, Number val)
	{
		if (!(column.getDataType().equals(DataType.DOUBLE)
				|| column.getDataType().equals(DataType.FLOAT)
				|| column.getDataType().equals(DataType.INTEGER)
				|| column.getDataType().equals(DataType.LONG)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.EQUALNUMBER, column, val, val, null);
	}
	
	/**
	 * Creates and returns a new instance of a Equal SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param val
	 *            the text value to search/filter by.
	 * 
	 * @return a new instance of a Equal SearchOption.
	 */
	public static SearchOption createSearchEqual(ColumnField column, String val)
	{
		if (!(column.getDataType().equals(DataType.STRING)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.EQUALSTRING, column, null, null, val);
	}
	
	/**
	 * Creates and returns a new instance of a Like SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param val
	 *            the text value to search/filter by.
	 * 
	 * @return a new instance of a Contains SearchOption.
	 */
	public static SearchOption createSearchContains(ColumnField column,
			String val)
	{
		if (!(column.getDataType().equals(DataType.STRING)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.CONTAINS, column, null, null,
				val);
	}
	
	/**
	 * Creates and returns a new instance of a Starts With SearchOption using
	 * the specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param val
	 *            the text value to search/filter by.
	 * 
	 * @return a new instance of a Starts With SearchOption.
	 */
	public static SearchOption createSearchStartsWith(ColumnField column,
			String val)
	{
		if (!(column.getDataType().equals(DataType.STRING)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.STARTS_WITH, column, null,
				null, val);
	}
	
	/**
	 * Creates and returns a new instance of a Ends With SearchOption using the
	 * specified parameters to build it.
	 * 
	 * @param column
	 *            the column to make the search on.
	 * @param val
	 *            the text value to search/filter by.
	 * 
	 * @return a new instance of a Ends With SearchOption.
	 */
	public static SearchOption createSearchEndsWith(ColumnField column,
			String val)
	{
		if (!(column.getDataType().equals(DataType.STRING)))
		{
			throw new IllegalArgumentException(
					"ColumnField DataType is incompatible with this search type!");
		}
		return new SearchOption(SearchOptionType.ENDS_WITH, column, null, null,
				val);
	}
}
